Announcing Android Activity Controller Preview

Today we're announcing our initial effort to make Activities in Android more friendly for .NET Developers. For now, we are calling it the ActivityController.

ActivityController is meant to act as a sort of replacement/proxy for AppCompatActivity. Currently, its primary purpose is to enable the use of async/await when using StartActivityForResult in an Activity, with the ability to survive the unpredictable lifecycle of activities on Android.

Inside your ActivityController you will see the new method StartActivityForResultAsync (Intent intent). You can await this. We've also added a few helper methods which will construct the appropriate Intent and call StartActivityForResultAsync<TResult> for you, returning a TResult with relevantly typed results. This includes:

Posts

Requesting activities to do some work on your behalf is one of those very primitives scenarios in the Android world. When you launch an activity that will give you back a result, you have to manually pick a handshake code, launch your activity and then override the OnActivityResult method. In this method, you would add handlers for your handshake codes. The whole thing is primitive (see samples here and here).

What I would love to do is to use await to take care of this for me, so I can say await TakePhotoAsync and not have to override methods, handle handshake codes and get a strongly typed return for it.

For this to work, instead of sublcassing Activity, you would subclass ControllerActivity as shown above.

You can see that you do not need the handshake, you do not need to override a method, you can use the delightful await in your code and you can use strongly typed return values! You can see the strongly typed returns that Jon did so far here and the various convenience async methods to invoke activities here.

Not only you can use await with these methods, but you can use await with any other awaitable code, and know that your application will not break when the device rotates, changes layout or anything else like that.

This works by making the ControllerActivity the long-term lived object, and the Activities are just the underlying Android representation that drives them. Activities come and go, but the ControllerActivity stays forever.

In my blog series I explained why it was important for this pattern to use Fragments. Xamarin.Forms doesn't use Fragments and suffers from a lot of issues as a result (loss of state when the Activity is destroyed). I suggest if you're going to promote a pattern like this you should go all the way and make it based on Fragments.

It won't. Even if you use Fragments to guard against Activity destruction/recreation, if the entire app is thrown out then this technique can't work. Using OnActivityResult directly you can handle that case, but it is still pretty hard to get right.

The one common case I've found where this comes up is when starting an activity for taking a picture on a device with a high-res camera. I think the Nexus 9 had a tendency to do this. After you take the picture the app that started the activity is thrown out, and it gets relaunched when the photo activity finishes. You have to resort to saving and restoring any important state in that case. I had to deal with this for Data Dashboard for LabVIEW.

My advice would be to stick to the lower level API for those challenging cases, but use this approach for the more common cases (especially in-process activities).

In my blog series I explained why it was important for this pattern to use Fragments. Xamarin.Forms doesn't use Fragments and suffers from a lot of issues as a result (loss of state when the Activity is destroyed). I suggest if you're going to promote a pattern like this you should go all the way and make it based on Fragments.

@adamkemp thanks I'll have a look at your blog post series and start to think about how fragments factor into this story.

The idea behind opening this up to the public so early is exactly for this kind of feedback! We want the community to bring forward use cases and ideas we may not have thought about with the long term goal of standardizing code and best practices into our core product once it makes sense.

The code linked from the blog series is licensed with CC0 (Creative Commons, zero attribution). Talk to your lawyers, but my intent is that it can be used for any purpose without restriction and without attribution. Take anything you want. That's why it's there.

As a current Apple employee I unfortunately can't contribute any more code to it at this point. I think my blog explains the reasoning behind the design and the problems I was solving so hopefully that can serve as adequate documentation.

We've hit our head hard with the whole async/await issues and the scenarios where this kind of coding pattern is not supported when the mobile platform evicts your app from memory. While I appreciate making this kind of thing easier for developers to stomach by using the familiar async/await pattern, I also think it is very important that Xamarin educates the developers that async/await is not going to work in all scenarios. The underlying mobile platform has got support for handling this scenario, but from what I can see these scenarios are not async/await compatible. So, by all means look at creating this kind of abstraction, but at the same time also educate where this is not going to work and talk about how the native platform makes provision for handling this scenario.

FWIW, within a single process (i.e., going from one activity to another where both come from the same app) this pattern should be safe (if you use fragments). Activities can get destroyed very easily (even with just configuration changes, like rotation), which means anyone using this pattern without fragments is asking for trouble. But your entire app won't be killed and relaunched unless you actually leave the app to start a new activity from some other app, so this pattern (with fragments) is safe for moving between activities within a single app. For cross-app use it's not safe.

@adamkemp the idea of the Activity Controller of course is to make sure the await context is captured in the controller instead of the activity instance so that the context persists things like config changes which cause the activity to be destroyed. So, Fragments aren't exactly necessary here, however we definitely want to adapt this pattern to Fragments too, but first we'd like iron out more details before implementing it in more places.

The one thing this doesn't address which has been mentioned is the potential of your app's process being killed. So, any time you're starting an activity from an external process you have the potential for this to happen. I believe in practice this is somewhat rare, but that doesn't mean we shouldn't be aware of the possibility. I think the best way to deal with this is to document it thoroughly.

@mveroukis you're right, it's not that hard to figure out, we're just trying to build a more sane/pleasant experience to work with. You are certainly free to ignore this approach and continue directly with Activities and Intents however Choice is good!

I see. Taking a closer look, now it seems like you may be reinventing what retained fragments already do. Android already has a mechanism for keeping track of and finding retained fragments by ID (i.e., the same as your registry), and it already has a system for passing down many (not all) of the activity methods to the contained fragment. Can you explain the thought process behind making a new concept instead of using the existing one?

@mveroukis, I explained some of the problems caused by the native approach in my blog post (linked above). The summary is:

Passing data in and out of the new activity has to be done via serialization/deserialization, which is error-prone and tedious to maintain.

You have to handle the activity result by overriding a method in an activity subclass, which forces you to have code in an activity to handle every possible other activity that may be launched from there. The Android framework is basically forcing you into coupling things that shouldn't need to be coupled. For instance, in our app we needed to launch a new activity from a popup presented on top of an activity. The popup is not an activity (it was a PopupWindow, I believe), so it couldn't by itself get the results of the activity it had just launched. Instead, we had to add code to each activity that showed the popup and then plumb through the results to send back to the popup. This is a lot of extra code and coupling that just doesn't make sense in a well-designed application. It's a huge pain.

This new approach has better type safety, decreases coupling, and greatly reduces the amount of boilerplate/plumbing code. It was a huge benefit for us in our app.

If you use retained fragments then there's no need to save state. That is yet another stupid flaw in Android's frameworks, and even Google now suggests that people use retained fragments instead. Seriously, you can save yourself a ton of error-prone code if you change your approach here. Our (complex) app didn't have to do any state saving for config changes or going between (in-process) activities, and it worked great. That's a lot of code that just disappeared.

Still more error-prone serialization and boilerplate code for something that should be simple and clean. Why make things harder?

I'm not trying to say that this stuff is hard to learn. Difficulty of learning it isn't the issue. The issue is that you end up with way more code, and that extra code is brittle. If you can remove the need for writing ~50 lines of code across 3+ files and get extra type safety in the process you should absolutely do it.

Sorry for derailing the thread, Jonathan. I just think it's worth explaining the benefits of this approach. The direction you're going is good, and it's going to be very valuable to Xamarin.Android developers if you do it well.

Not sure I'd say that Android's framework is "stupid" in this regard. Also, Google's recommendation is to use retained fragments in addition to other techniques, including the saved state bundle and also to handle configuration changes yourself (for instance, if you use the same layout and other resources regardless of orientation, no need to force a kill/create cycle at all).

I could be wrong, but it's my understanding that retained fragments are not well suited for cases where a user moves away from an app (say, phone call) and returns to is after Android has already shut down the activity. The retained fragment would also be shut down (otherwise we'd have crazy memory leaks everywhere) and thus the state would be lost. You'd want to use the saved state bundle AND the retained fragment - the fragment provides a quick re-initialization and the saved state adds robustness as you'd store just wants needed to reload the data from server or local DB.

Anyway, sorry for taking this way off topic. I think my main objection here is that Activities and Intents are the building blocks of Android and in my opinion are quite brilliant in their design. I may be biased as I've done more native Java Android than C#. I can see why reducing boilerplate code is valuable, I'm just wondering if there are other ways that don't abstract away the power of Intents.

I could be wrong, but it's my understanding that retained fragments are not well suited for cases where a user moves away from an app (say, phone call) and returns to is after Android has already shut down the activity.

Retained fragments do work in that case as long as the app isn't killed. However, they don't work if the entire app is shut down (which can happen), so in the case where you are launching an activity out of process you really have to do it the hard way anyway. As I said before, you're right about that. I don't condone anyone trying to use this approach or anything like it for out-of-process activities unless you want buggy apps.

Intents are very powerful for cross-process UI sharing, but they're really terrible for going from screen to screen within the same app. The "stupid" thing that Google did was to try to treat those two use cases as the same thing. The result is that developers have to deal with the powerful but difficult and brittle mechanism even for cases where you're never leaving the app. People who do iOS development first (myself included) are appalled at how difficult it is on Android to simply go from one screen to another and back (sending data in and getting data back) compared to the same thing in iOS.

The bottom line is that for the in-process use case there really is no risk to this approach, but you are right that it shouldn't absolve anyone of learning the underlying API because you probably need to do that in some cases anyway, and you should really understand when to use each approach.

After taking a first look, I am with @mveroukis. Android already provides what is needed to interact with different activities and passing data back and forth. But I see what @adamkemp is trying to say.

Doing iOS and Android coding I personally don't like the term "Controller" in the context of Android. And instead of putting energy in this new project I would like to see faster releases for new Android or Google Play Services versions. Don't get me wrong. Anything that saves me from writing unnecessary code is welcome. But at first hand I like to be able to compete with Java, Swift or Objective-c apps when it comes to release dates and implementing new platform features. And by looking at Xamarin.Forms and how long it took to get Material Design support, I would say I fear that the same could potentially happen here again.

I tend to agree with @SebastianSeidel.9226, the use of "Controller" in the name is kinda weird for Android, it seems like an iOS naming convention. To add to the confusion, we have a class called ActivityController and one called ControllerActivity. There has got to be a better way to name these.