Paco Paco - 5 months ago 267
Android Question

Send Data from a Service to BroadcastReceiver of other Activity (Xamarin Android)

I have a problem with sending

Location
from service to custom BroadcastReceiver.

This is my BroadcastReceiver.cs

[BroadcastReceiver]
class MyBroadcastReceiver : BroadcastReceiver
{
public static readonly string GRID_STARTED = "GRID_STARTED";
public event EventHandler<OnLocationChangedEventArgs> mOnLocationChanged;
private Location location;
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action == GRID_STARTED)
{
Toast.MakeText(context, "Grid Started", ToastLength.Short).Show();
//location = JsonConvert.DeserializeObject<Location>(intent.GetStringExtra("location"));
//mOnLocationChanged.Invoke(this, new OnLocationChangedEventArgs(location));
}
}
}


If I UNCOMMENT two lines in the upper code my app suddenly stops. I cannot tell you what is the error because, while developing Xamarin apps debugging is stopped by an internal error (I read about it on Xamarin Forums but couldn't find time to deal with it).

This is what I have done in service:

private void BroadcastStarted(Location location)
{
Intent BroadcastIntent = new Intent(this, typeof(MyBroadcastReceiver));
BroadcastIntent.PutExtra("location",JsonConvert.SerializeObject(location));
BroadcastIntent.SetAction(MyBroadcastReceiver.GRID_STARTED);
BroadcastIntent.AddCategory(Intent.CategoryDefault);
SendBroadcast(BroadcastIntent);
}


I'm using Newtonsoft.Json for sending an objet.
Any help would be appreciated.

UPDATE:

Ok, somehow I managed to reveal the error:


Unable to find a constructor to use for type
Android.Location.Location. A class should either have a default
constructor,one constructor with arguments or a constructor marked
with JsonConstructor attribute.


UPDATE:

Whole service code:

using Newtonsoft.Json;

namespace GoogleMaps
{
public class OnLocationChangedEventArgs
{
Location location;

public Location Location
{
get { return location; }
set { location = value; }
}

public OnLocationChangedEventArgs(Location location)
{
this.location = location;
}
}

[Service]
class MyService : Service
{
private LocationManager locationManager = null;

public MyService()
{

}

private class MyLocationListener : Java.Lang.Object,ILocationListener
{
Location mLastLocation;
public event EventHandler<OnLocationChangedEventArgs> onLoc;

public MyLocationListener(String provider)
{
mLastLocation = new Location(provider);
}

public void OnLocationChanged(Location location)
{
try
{
mLastLocation.Set(location);
onLoc.Invoke(this, new OnLocationChangedEventArgs(mLastLocation));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

public void OnProviderDisabled(string provider)
{

}

public void OnProviderEnabled(string provider)
{

}

public void OnStatusChanged(string provider, [GeneratedEnum] Availability status, Bundle extras)
{

}
}

private MyLocationListener locationListener = new MyLocationListener("network");

public override IBinder OnBind(Intent intent)
{
return null;
}
private void BroadcastStarted(Location location)
{
Intent BroadcastIntent = new Intent(this, typeof(MyBroadcastReceiver));
BroadcastIntent.PutExtra("location",JsonConvert.SerializeObject(location));
BroadcastIntent.SetAction(MyBroadcastReceiver.GRID_STARTED);
BroadcastIntent.AddCategory(Intent.CategoryDefault);
SendBroadcast(BroadcastIntent);
}

[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
return StartCommandResult.Sticky;
}

public override void OnCreate()
{
try
{
base.OnCreate();
InitializeLocationManager();

locationManager.RequestLocationUpdates(LocationManager.NetworkProvider, 0, 0, locationListener);

locationListener.onLoc += MyService_onLoc;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

private void MyService_onLoc(object sender, OnLocationChangedEventArgs e)
{
BroadcastStarted(e.Location);
}

public override void OnDestroy()
{
base.OnDestroy();
locationManager.RemoveUpdates(locationListener);
}

private void InitializeLocationManager()
{
if (locationManager == null)
{
locationManager = (LocationManager)GetSystemService(LocationService);
}
}
}
}


UPDATE:

This is what I told in 6th comment:

public override void OnReceive(Context context, Intent intent)
{
if (intent.Action == GRID_STARTED)
{
try
{
Toast.MakeText(context, "Grid Started", ToastLength.Short).Show();

a = new LatLng(intent.GetDoubleExtra("latitude",0),intent.GetDoubleExtra("longitude",0));
mOnLocationChanged.Invoke(this, new OnLatLngChangedEventArgs(a)); // NULL EXCEPTION LINE
}
catch (Exception ex)
{
Toast.MakeText(context, ex.Message, ToastLength.Short).Show();
}
}
}


Why is event handler mOnLocationChanged equal to null?
And service's part:

private void BroadcastStarted(Location location)
{
Intent BroadcastIntent = new Intent(this, typeof(MyBroadcastReceiver));
BroadcastIntent.PutExtra("latitude",location.Latitude);
BroadcastIntent.PutExtra("longitude", location.Longitude);
BroadcastIntent.SetAction(MyBroadcastReceiver.GRID_STARTED);
BroadcastIntent.AddCategory(Intent.CategoryDefault);
SendBroadcast(BroadcastIntent);
}

Answer

Send data (not object) from Service (using SendBroadcast) to BroadcastReceiver (in MainActivity):

Android-java Gist here. (100% working and tested code).

C# equivalent Service Class code:

(see import statements in gist for required namespaces/classes)

[Service]
public class BackgroundService : Service {
    private static LocationReceiver mTickReceiver;

    public BackgroundService()
    {

    }




    public override IBinder OnBind(Intent arg0)
    {
        return null;
    }


 public override StartCommandResult OnStartCommand (Android.Content.Intent intent, StartCommandFlags flags, int startId)
 {


        return StartCommandResult.Sticky;
  }


    public override Void OnCreate()
    {
        registerReceiver();
    }


    public override Void OnDestroy()
    {
        UnregisterReceiver(mTickReceiver);
        mTickReceiver = null;
    }



    private void registerReceiver()
    {

       mTickReceiver= new LocationReceiver();

        IntentFilter filter = new IntentFilter(Android.Content.Intent.ActionTimeTick); // this will broadcast Intent every minute
        RegisterReceiver(mTickReceiver, filter);
    }

    // you can write this class in separate cs file


    [BroadcastReceiver(Enabled = true)]

    [IntentFilter(new[] { Android.Content.Intent.ActionTimeTick })]

    public class LocationReceiver : BroadcastReceiver
    {

        public override Void OnReceive(Context context, Intent intent)
        {

            // sample data, you should get your location here,
            // one way is to implement location logic in this class
                   double SampleLatitude=52.01566;
                    double SampleLongitude=65.00487;

                    // assuming above coordinates are from some location manager code
                    Intent i=new Intent();
                    i.SetAction("LocationData");
                    i.PutExtra("Latitude",SampleLatitude);
                    i.PutExtra("Longitude",SampleLongitude);
                    // PREPARE BROADCAST FOR MAINACTIVITY
                    SendBroadcast(i);  // this broadcast will be received by mainactivity
        }
    }

    }

C# equivalent MainActivity Class code:

(see import statements in gist for required namespaces/classes)

    public class MainActivity : AppCompatActivity {




        protected override Void OnCreate(Bundle savedInstanceState) 
        {
            base.OnCreate(savedInstanceState);
            SetContentView(R.layout.activity_main);

           Intent i=new Intent(this, typeof(BackgroundService));
            StartService(i);
            IntentFilter filter = new IntentFilter("LocationData");
            RegisterReceiver(new MyBroadcastReceiver(),filter);

        }

    // public static variables of MainActivty can be accessed and manipulated in this class
        [BroadcastReceiver(Enabled = true)]        
        [IntentFilter(new[] { "LocationData" })]
     class MyBroadcastReceiver : BroadcastReceiver
     {

          public override Void OnReceive(Context context, Intent intent)
          {
            // GET BROADCAST FROM RECEIVER IN THE BACKGROUND SERVICE CLASS
            if (intent.GetAction() == "LocationData")
            {
                double lat=intent.GetDoubleExtra("Latitude",0);
                double lng=intent.GetDoubleExtra("Longitude",1);

                String LocationDataFromService=lat+","+lng;
                // REPLACE this with console.writeline
                Log.d("LocationDataFromService",LocationDataFromService);  


           }
        }
    }
}

In AndroidManifest.xml declare service as:

<service android:name=".BackgroundService">
    </service>

It may still throw some errors. Hope this helps.

Comments