locked
Announcing Android Activity Controller Preview RRS feed

  • Question

  • User44363 posted

    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:

    ```csharp [Activity(MainLauncher = true, Label = "Your Activity", Theme = "@style/Theme.AppCompat")] public class MainActivity : ControllerActivity { 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.

    Friday, December 9, 2016 3:51 AM

All replies

  • User13 posted

    I wanted to share the rationale for this work.

    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.

    This means that the ugly mess that used to look like this: ``` const int photoHandshake = 123; void TakePic () { var intent = new Intent(MediaStore.ActionImageCapture); StartActivityForResult (intent, photoHandshake); }

    protected override void OnActivityResult (int requestCode, Result resultCode, Intent data) { base.OnActivityResult (requestCode, resultCode, data); if (requestCode == photoHandshake && resultCode == Result.Ok){ // Now, extract the url like an animal SaveUrl (Data.Data); } } ```

    Can be now written like this:

    async void TakePic () { var media = await TakePhotoAsync ("Smile"); SaveUrl (media.SelectedMediaUri); }

    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.

    Friday, December 9, 2016 7:22 PM
  • User98607 posted

    @MigueldeIcaza What if using AppCompatActivity ?

    Friday, December 9, 2016 7:32 PM
  • User25759 posted

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

    Friday, December 9, 2016 7:34 PM
  • User66766 posted

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

    Friday, December 9, 2016 7:52 PM
  • User98607 posted

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

            SetSupportActionBar(toolbar);
            SupportActionBar.SetDisplayShowTitleEnabled(false);
            SupportActionBar.SetDisplayHomeAsUpEnabled(false);
    
    Friday, December 9, 2016 10:59 PM
  • User44363 posted

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

    Saturday, December 10, 2016 1:39 AM
  • User44363 posted

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

    Saturday, December 10, 2016 1:53 AM
  • User6349 posted

    EWWWWWWWWW... async. :tongue: :tongue: :tongue: https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/9126493-improve-asynchronous-programming-model

    Saturday, December 10, 2016 3:20 AM
  • User181 posted

    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.

    Saturday, December 10, 2016 7:14 AM
  • User30447 posted

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

    Saturday, December 10, 2016 7:42 AM
  • User181 posted

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

    Saturday, December 10, 2016 8:40 AM
  • User2148 posted

    @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

    Saturday, December 10, 2016 8:58 AM
  • User44363 posted

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

    Saturday, December 10, 2016 3:15 PM
  • User181 posted

    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.

    Sunday, December 11, 2016 12:23 AM
  • User30447 posted

    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.

    Sunday, December 11, 2016 5:17 PM
  • User181 posted

    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.

    Tuesday, December 13, 2016 10:56 PM
  • User44363 posted

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

    Thursday, December 15, 2016 3:23 PM
  • User238006 posted

    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.

    Thursday, December 15, 2016 6:12 PM
  • User44363 posted

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

    Thursday, December 15, 2016 6:17 PM
  • User181 posted

    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?

    Thursday, December 15, 2016 6:19 PM
  • User181 posted

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

    Thursday, December 15, 2016 6:26 PM
  • User238006 posted
    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

    Thursday, December 15, 2016 7:44 PM
  • User181 posted
    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.

    Thursday, December 15, 2016 8:47 PM
  • User238006 posted

    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.

    Thursday, December 15, 2016 9:12 PM
  • User181 posted

    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.

    Thursday, December 15, 2016 10:29 PM
  • User1341 posted

    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.

    Wednesday, December 28, 2016 11:48 AM
  • User238006 posted

    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.

    Tuesday, January 3, 2017 11:06 PM
  • User332005 posted

    Any updates?

    Friday, July 21, 2017 5:35 PM
  • User353788 posted

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

    Tuesday, October 17, 2017 11:11 AM
  • User364293 posted

    what's this

    Thursday, January 4, 2018 9:08 AM
  • User365346 posted

    any updates?

    Thursday, February 8, 2018 8:26 AM
  • User370681 posted

    The previous comments are worth but whats the further update can any1 update it further please ?

    Wednesday, July 11, 2018 2:16 PM
  • User371031 posted

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

    Wednesday, July 25, 2018 2:45 AM
  • User371156 posted

    I'm also looking forward to new updates

    Monday, July 30, 2018 3:39 AM
  • User377602 posted

    Thank you for the information.

    Saturday, September 15, 2018 8:58 PM