Announcing Android Activity Controller Preview

JonathanDickJonathanDick CAXamarin Team, Developer Group Leader Xamurai

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.

We're looking for dialog, feedback, suggestions, and contributions around this concept. The source code for the project can be found on GitHub here: https://github.com/xamarin/android-activity-controller

The boilerplate for using ActivityController looks something like this:

[Activity(MainLauncher = true, Label = "Your Activity", Theme = "@style/Theme.AppCompat")]
public class MainActivity : ControllerActivity<MainActivity.MainController>
{
    public class MainController : ActivityController
    {
        protected override void OnCreate(Android.OS.Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            SetContentView(Resource.Layout.FirstLayout);

            // Your code     
        }
    }
}

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:

  • PickContactAsync
  • PickPhotoAsync
  • TakePhotoAsync
  • PickVideoAsync
  • TakeVideoAsync

There is a prerelease package on NuGet. Please try it out!

If you have comments, feedback, or suggestions, please create a new GitHub Issue to discuss.

Posts

  • BerayBentesenBerayBentesen TRUniversity ✭✭✭✭

    @MigueldeIcaza What if using AppCompatActivity ?

  • JamesMontemagnoJamesMontemagno USForum Administrator, Xamarin Team, Developer Group Leader Xamurai

    @BerayBentesen "ActivityController is meant to act as a sort of replacement/proxy for AppCompatActivity", which means it is based on AppCompatActivity at it's core!

  • DH_HA1DH_HA1 USMember ✭✭✭

    @JamesMontemagno @MigueldeIcaza can this be used with Xamarin.Forms MainActivity?

  • BerayBentesenBerayBentesen TRUniversity ✭✭✭✭

    @JamesMontemagno Thanks but following codes does not working for example :

            SetSupportActionBar(toolbar);
            SupportActionBar.SetDisplayShowTitleEnabled(false);
            SupportActionBar.SetDisplayHomeAsUpEnabled(false);
    
  • JonathanDickJonathanDick CAXamarin Team, Developer Group Leader Xamurai

    @BerayBentesen you can access the instance of the AppCompatActivity directly through the Activity property on the controller to call these methods.

  • JonathanDickJonathanDick CAXamarin Team, Developer Group Leader Xamurai

    @DH_HA1 not currently but I think it's probably worth exploring allowing other Activity types like the forms main activity type.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    This looks familiar...

    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.

  • CarelLotzCarelLotz ZAMember ✭✭

    How will this work if my application is evicted from memory due to memory pressure whilst awaiting a result?

  • adamkempadamkemp USInsider, Developer Group Leader mod

    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).

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @adamkemp said:
    This looks familiar...

    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 is always a pleasure to hear you

  • JonathanDickJonathanDick CAXamarin Team, Developer Group Leader Xamurai

    @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.

    Thank you for your feedback!

  • adamkempadamkemp USInsider, Developer Group Leader mod

    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.

  • CarelLotzCarelLotz ZAMember ✭✭

    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.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    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.

    Also, the Media Plugin (https://github.com/jamesmontemagno/MediaPlugin/blob/master/src/Media.Plugin.Android/MediaPickerActivity.cs) has had this same bug for probably as long as it's existed. I think I reported it at some point. Using either events or async/await to wait for results from activities outside your app is inherently unsafe. It should not be relied on or encouraged.

  • JonathanDickJonathanDick CAXamarin Team, Developer Group Leader Xamurai

    @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.

  • mveroukismveroukis CAMember ✭✭✭

    Personally, I like working with Intents directly and find little value with this new approach. With a few simple examples, StartActivityForResult isn't that hard to figure out.

  • JonathanDickJonathanDick CAXamarin Team, Developer Group Leader Xamurai

    @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!

  • adamkempadamkemp USInsider, Developer Group Leader mod

    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?

  • adamkempadamkemp USInsider, Developer Group Leader mod

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

    1. Passing data in and out of the new activity has to be done via serialization/deserialization, which is error-prone and tedious to maintain.
    2. 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.

  • mveroukismveroukis CAMember ✭✭✭
    1. Serializing via the parcelable interface can be useful for saving state, so it's probably something people should be familiar with.

    2. Wouldn't a local broadcast receiver be useful in that scenario?

    https://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html

  • adamkempadamkemp USInsider, Developer Group Leader mod
    1. 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.
    2. 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.

  • mveroukismveroukis CAMember ✭✭✭
    edited December 2016

    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.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    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.

  • SebastianSeidel.9226SebastianSeidel.9226 DEInsider, University ✭✭✭✭

    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.

  • mveroukismveroukis CAMember ✭✭✭

    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.

  • Any updates?

  • Jay2Jay2 AUMember ✭✭
    edited October 17

    Also, I want to know if new updates are available?

Sign In or Register to comment.