【问题标题】:Send Data from a Service to BroadcastReceiver of other Activity (Xamarin Android)将数据从服务发送到其他活动的广播接收器(Xamarin Android)
【发布时间】:2016-06-08 06:12:48
【问题描述】:

我在将 Location 从服务发送到自定义 BroadcastReceiver 时遇到问题。

这是我的 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));
        }
    }
}

如果我取消注释上面代码中的两行,我的应用程序会突然停止。我无法告诉您错误是什么,因为在开发 Xamarin 应用程序时,调试因内部错误而停止(我在 Xamarin 论坛上读到过它,但找不到时间来处理它)。

这是我在服务中所做的:

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);
}

我正在使用 Newtonsoft.Json 发送一个对象。 任何帮助将不胜感激。

更新

好的,不知何故我设法揭示了错误:

找不到用于类型的构造函数 安卓.位置.位置。一个类应该有一个默认值 构造函数,带参数的构造函数或标记的构造函数 带有 JsonConstructor 属性。

更新

整个服务代码:

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);
        }
    }
}
}

更新

这是我在第 6 条评论中所说的:

 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();
            }
        }
 }

为什么事件处理程序 mOnLocationChanged 等于 null? 和服务的一部分:

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);
}

【问题讨论】:

  • 服务中位置在哪里初始化?
  • 我在 LocationListener 中获取位置,然后通过 eventHandler 将其传递给服务
  • 所以 LocationListener 将“位置”对象发送到 BroadcastStarted(Here) ?可以发一些代码吗?
  • 我已经更新了我的问题
  • 我认为这仅在位置更改时才有效,否则 mLastLocation 中将没有任何值,因为它仅在 onLocationChange 事件触发时设置。尝试通过在设备或模拟器上测试来模拟位置变化。

标签: c# android service xamarin broadcastreceiver


【解决方案1】:

将数据(不是对象)从Service(使用SendBroadcast)发送到BroadcastReceiver(在MainActivity中):

Android-java Gist here.(100% 工作和测试代码)。

C# 等效服务类代码:

(请参阅要点中的导入语句以了解所需的命名空间/类)

[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#等效的MainActivity类代码:

(请参阅要点中的导入语句以了解所需的命名空间/类)

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


           }
        }
    }
}

AndroidManifest.xml 中声明服务为:

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

它可能仍然会抛出一些错误。希望这会有所帮助。

【讨论】:

  • 如何从broadcastReceiver 获得持续的信息?在这一点上,我只获得一次信息。
  • 我必须提醒你,这是 ciclus:MainActivity 启动一个服务,服务每分钟获取一次位置,并定期将经纬度事件广播到广播接收器,然后广播接收器应该发送回位置到 MainActivity,它将使用给定位置更新 TextView。这可能吗?
  • 为什么需要服务?需要后台运行吗?否则只需在 MainActivity 中实现 onLocationChange() ,它总是(每次)在位置更改时触发。无论您更改位置一次还是 10 次。
  • 是的,我需要在后台获取位置。
  • 看起来我们被困在这里了,我们仍然需要位置对象进行第一次调用。有什么我会回复你的。
【解决方案2】:

您也可以在MyBroadcastReceiver.cs中实现接口。我认为它更简单。

代码如下:

MyBroadcastReceiver.cs

[BroadcastReceiver]
class MyBroadcastReceiver : BroadcastReceiver
{
    public interface LocationDataInterface
    {
        void OnLocationChanged(LatLng point);
    }

    public static readonly string GRID_STARTED = "GRID_STARTED";

    private LocationDataInterface mInterface;

    private LatLng a;
    public override void OnReceive(Context context, Intent intent)
    {
        if (intent.Action == GRID_STARTED)
        {
            try
            {
                // data you got from background service
                a = new LatLng(intent.GetDoubleExtra("latitude",0), intent.GetDoubleExtra("longitude",0));

                mInterface = (LocationDataInterface)context;
                mInterface.OnLocationChanged(a);
            }
            catch (Exception ex)
            {
                Toast.MakeText(context, ex.Message, ToastLength.Short).Show();
            }
        }
    }
}

MainActivity.cs

public class MainActivity : Activity, MyBroadcastReceiver.LocationDataInterface
{
      ...
      public void OnLocationChanged(LatLng point)
      {
           // textview where you want to show location data
           locationText.Text += point.Latitude + "," + point.Longitude; 
           // things that you want to do with location point
      }      
}

如果此方法存在一些问题,请随时发表评论。

【讨论】:

  • 这是一个不错的解决方案,但我得到的是应用程序类型上下文,而不是当前打开的当前活动上下文。
  • @Mohsinkhan 帮不上忙,因为我没有看到代码,但是,如果您喜欢解决方案,请投票。
猜你喜欢
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-01
  • 1970-01-01
  • 2012-10-10
  • 1970-01-01
相关资源
最近更新 更多