简体   繁体   中英

Xamarin Android: using StartActivityForResult(); resultCode in OnActivityResult is always “Canceled”

I have two separate applications written using Xamarin.Android; for the sake of discussion, let's call them "Tristan" and "Isolde". Tristan has some state information that Isolde sometimes needs to know. Complication: Tristan may or may not be running at the moment Isolde develops the need to know his state.

I've got kludge working now where Isolde sends a special launch intent to Tristan, who then uses a broadcast intent to send information back to Isolde. (See my earlier question for details.)

"But wait!" I hear you cry, "Is this not a perfect use case for StartActivityForResult() ?" Indeed it is! The code is a whole lot simpler, and everything I've read implies that this is how Android wants you to do stuff like this.

Unfortunately, I can't get it to work (despite trying many variations and reading the dozen-or-so related questions on this very site). My specific problem is that in Isolde's OnActivityResult() callback, the resultCode is always Result.Canceled and the data is always null .

Here is the code for Tristan (where commented-out bits represent variations I've tried):

using Android.App;
using Android.Content;

namespace com.example.Tristan.Android
{
    [Activity(Name ="com.example.Tristan.Android.IsoldeQueryActivity")]
    public class IsoldeQueryActivity : Activity
    {
        protected override void OnStart()
        {
            // base.OnStart();
            var rtn = new Intent();
            rtn.PutExtra("Test", "test");
            //rtn.SetAction("TestAction");
            SetResult(Result.Ok, rtn);
            Finish();
            //FinishActivity(1234);
        }
    }
}

And here is the relevant code from the Activity where Isolde needs to ask for Tristan's state:

    private TaskCompletionSource<bool> TristanStateCompletion;

    public async Task GetTristanState()
    {
        TristanStateCompletion = new TaskCompletionSource<bool>();

        var req = new Intent("com.example.Tristan.Android.IsoldeQueryActivity");
        //req.PutExtra(Intent.ExtraReturnResult, true);
        StartActivityForResult(req, 1234);
        var rtn = await TristanStateCompletion.Task;
        if (!rtn) bomb("can't get state");
        TristanStateCompletion = null;
    }

    protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);
        if(requestCode == 1234) {
           DoStuffWith(data);
           TristanStateCompletion?.TrySetResult(true);
        }
    }

Diagnostics -- or rather, a specific lack of them -- leads me to believe that Tristan's IsoldeQueryActivity.OnStart() is never actually being called.

Ideas, requests for additional information and/or useful experiments to try are all welcome. (If your idea is "Put <thing> in the manifest", remember this is Xamarin.Android and I have to do that by putting <relatedThing> in the attribute decorating the Activity .)

Edited to add: In Isolde's code, DoStuffWith(data) was crashing because data was null . When I changed that method to avoid that, I found that I got a (slightly later) exception thrown in StartActivityForResult() :

Android.Content.ActivityNotFoundException No Activity found to handle Intent { act=com.example.Tristan.Android.IsoldeQueryActivity }

This leads me to believe I'm not creating the Intent properly in Isolde. Do I need to be using one of the other Intent constructors? If so, how specifically?

Okay, I think I have this figured out. The code in my original question had three major problems:

  1. I was building the Intent incorrectly in Isolde.
  2. I didn't export the IsoldeQueryActivity in Tristan.
  3. The call to base.OnStart() in Tristan's OnStart override is mandatory.

Here is the working version of Tristan:

using Android.App;
using Android.Content;

namespace com.example.Tristan.Android 
{
    [Activity(Name ="com.example.Tristan.Android.IsoldeQueryActivity", Exported=true)]
    public class IsoldeQueryActivity : Activity
    {
        protected override void OnStart()
        {
            base.OnStart();
            var rtn = new Intent();
            rtn.PutExtra("Test", "test");
            SetResult(Result.Ok, rtn);
            Finish();
        }
    }
}

And here is the fixed code from Isolde:

private TaskCompletionSource<bool> TristanStateCompletion;

public async Task GetTristanState()
{
    TristanStateCompletion = new TaskCompletionSource<bool>();

    var req = new Intent();
    req.SetComponent(new ComponentName("com.example.Tristan.Android", "com.example.Tristan.Android.IsoldeQueryActivity"));

    StartActivityForResult(req, 1234);
    var rtn = await TristanStateCompletion.Task;
    if (!rtn) bomb("can't get state");
    TristanStateCompletion = null;
}

protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
    if(requestCode == 1234) {
       if(resultCode != Result.Ok) bomb("bad resultCode {0}", resultCode);
       if(data == null) bomb("null data from Tristan");
       DoStuffWith(data);
       TristanStateCompletion?.TrySetResult(true);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM