2

我有一个名为“BatteryManagerHelper”的静态类,它通过 Intent.ACTION_BATTERY_CHANGED 意图监控电池状态。每次系统通知电池状态发生变化时,我的班级都会捕获变化,将新值存储在班级公共属性(充电、状态、健康..)中,然后引发事件“已更改”

公共静态事件操作已更改;

我项目中的每个 Activity 或 Class 都可以使用代码订阅此事件:

BatteryManagerHelper.Changed += BatteryManagerHelper_Changed;

private void BatteryManagerHelper_Changed()
{
  imgBattery.SetImageResource(some_resource);
}

它工作正常。

但是,当 Activity 进入 OnPause() 或 OnDestory() 时,我想取消订阅 Change 事件,因此不再向我的 Activity 发出通知。

我试过了

BatteryManagerHelper.Changed -= BatteryManagerHelper_Changed;

BatteryManagerHelper.Changed -= null;

BatteryManagerHelper.Changed += null;

但似乎没有人工作。我在 Activity 中也得到了事件,已经从系统中销毁。

所以,第一个问题是:如何正确取消订阅事件?第二个问题是:为什么一个被销毁的activity可以继续接收事件?

注意:订阅和取消订阅代码放在 OnResume() 和 OnPause() 上。当活动暂停然后恢复时,我收到两个事件通知;在第一次暂停恢复时,我收到三个连续的通知,依此类推。似乎 += 正在添加一个新的事件侦听器,而 -= 无法删除侦听器,从而使以前的侦听器引用保持有效。

这是静态类代码。我使用了一个静态类,因此可以在代码中的任何位置访问它。静态类(显然)有一个用于 BroadcastReceiver 回调的子类的私有实例。

using Android.Content;
using Android.Os;

using System;

namespace MenuDroidApp
{
  public static class BatteryManagerHelper
  {   
    private static BatteryBroadcastReceiverHelper batteryBroadcastReceiver = null;

    private static Intent batteryStatusIntent = null;

    public static event Action Changed;

    public enum HealthEnum    
    { 
      Unknown = 1,            // BATTERY_HEALTH_UNKNOWN
      Good,                   // BATTERY_HEALTH_GOOD
      OverHeat,               // BATTERY_HEALTH_OVERHEAT
      Dead,                   // BATTERY_HEALTH_DEAD
      OverVoltage,            // BATTERY_HEALTH_OVER_VOLTAGE
      UnspecifiedFailure,     // BATTERY_HEALTH_UNSPECIFIED_FAILURE
      Cold,                   // BATTERY_HEALTH_COLD
    }

    public enum PluggedEnum    
    { 
      NotPlugged = 0,
      PluggedAC,              // BATTERY_PLUGGED_AC
      PluggedUSB,             // BATTERY_PLUGGED_USB
      PluggedWireless,        // BATTERY_PLUGGED_WIRELESS
    }

    public enum StatusEnum    
    { 
      Unknown = 1,            // BATTERY_STATUS_UNKNOWN
      Charging,               // BATTERY_STATUS_CHARGING
      Discharging,            // BATTERY_STATUS_DISCHARGING
      NotCharging,            // BATTERY_STATUS_NOT_CHARGING
      Full,                   // BATTERY_STATUS_FULL     
    }

    public static int             Charge      { get; set; }    
    public static StatusEnum      Status      { get; set; }
    public static PluggedEnum     Plugged     { get; set; }
    public static HealthEnum      Health      { get; set; }
    public static bool            Present     { get; set; }
    public static string          Technology  { get; set; }
    public static int             Temperature { get; set; }
    public static int             Voltage     { get; set; }
    public static int             IconResID   { get; set; }

    static BatteryManagerHelper()
    {
      batteryBroadcastReceiver  = new BatteryBroadcastReceiverHelper();

      batteryBroadcastReceiver.Changed += batteryBroadcastReceiver_Changed;
    }

    private static void batteryBroadcastReceiver_Changed()
    {
      if (Changed != null) Changed();
    }

    public static void Start()
    {       
      batteryStatusIntent = Common.ApplicationContext.RegisterReceiver(batteryBroadcastReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

      batteryBroadcastReceiver.GetBatteryInfo(batteryStatusIntent);      
    }

    public static void Stop()
    {       
      Common.ApplicationContext.UnregisterReceiver(batteryBroadcastReceiver);
      batteryStatusIntent = null;
    }

    private class BatteryBroadcastReceiverHelper : BroadcastReceiver
    {
      public event Action Changed;

      public override void OnReceive(Context context, Intent intent)
      {
        GetBatteryInfo(intent);
      }

      public void GetBatteryInfo(Intent intent)
      { 
        BatteryManagerHelper.Charge       = intent.GetIntExtra(BatteryManager.EXTRA_LEVEL,  -1);
        BatteryManagerHelper.Status       = (StatusEnum)intent.GetIntExtra(BatteryManager.EXTRA_STATUS, 0);
        BatteryManagerHelper.Health       = (HealthEnum)intent.GetIntExtra(BatteryManager.EXTRA_HEALTH, 0);       
        BatteryManagerHelper.Plugged      = (PluggedEnum)intent.GetIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
        BatteryManagerHelper.Present      = intent.GetBooleanExtra(BatteryManager.EXTRA_PRESENT, false);
        BatteryManagerHelper.Technology   = intent.GetStringExtra(BatteryManager.EXTRA_TECHNOLOGY);
        BatteryManagerHelper.Temperature  = intent.GetIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
        BatteryManagerHelper.Voltage      = intent.GetIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);        
        BatteryManagerHelper.IconResID    = intent.GetIntExtra(BatteryManager.EXTRA_ICON_SMALL, -1);        


        if (Changed != null) Changed();
      }
    }  
  }

}

这是我的片段中的代码(这个片段是系统工具栏的替代品,我的应用是全屏的),但您也可以在经典的 Activity 类中尝试 OnResume() 和 OnPause() 事件

public override void OnAttach(Activity activity)
{
  BatteryManagerHelper.Changed += BatteryManagerHelper_Changed;
}

public override void OnDetach()
{
  BatteryManagerHelper.Changed -= BatteryManagerHelper_Changed;
}

private void BatteryManagerHelper_Changed()
{
  imgBattery.SetImageResource(some_resource_id);
}

好的,我在订阅/取消订阅事件时尝试了不同的方法:

private Action batteryManager_Callback = null;

public override void OnResume()
{
  base.OnResume();

  batteryManager_Callback = new Action(BatteryManagerHelper_Changed);
  BatteryManagerHelper.Changed += batteryManager_Callback;
}

public override void OnPause()
{
  base.OnPause();

  BatteryManagerHelper.Changed -= batteryManager_Callback;
}

private void BatteryManagerHelper_Changed()
{
  imgBattery.SetImageResource(some_res_id);
}

现在它工作正常。我为每个 OnResume 活动事件使用一个新的委托对象,并使用该委托订阅 BatteryManager 事件。委托引用也存储在私有类成员上。当我想取消订阅时,我使用在 OnResumed() 中创建的相同原始委托引用,并且我确信将从事件侦听器队列中删除相同的一个特定委托。

所以看起来原始代码

public override void OnAttach(Activity activity)
{
  BatteryManagerHelper.Changed += BatteryManagerHelper_Changed;
}

public override void OnDetach()
{
  BatteryManagerHelper.Changed -= BatteryManagerHelper_Changed;
}

回调函数 BatteryManagerHelper_Changed() 的引用在 += 的时刻和 -= 的时刻是不同的。但是 Activity 类实例是相同的,这很奇怪.. 这个 C#/.NET 环境中的示例运行良好。

关于这种行为的任何想法?这是设计正确的吗?

谢谢

4

1 回答 1

1

当你编译这段代码时:

private void BatteryManagerHelper_Changed()
{
  imgBattery.SetImageResource(some_res_id);
}

BatteryManagerHelper.Changed += BatteryManagerHelper_Changed;

那么 rhs 不是委托类型,它会被一个 Action 的实例隐式包装。退订时也会发生同样的情况:

BatteryManagerHelper.Changed -= BatteryManagerHelper_Changed;

执行此操作时不会发生这种隐式包装:

batteryManager_Callback = new Action(BatteryManagerHelper_Changed);
...
BatteryManagerHelper.Changed += batteryManager_Callback;
...
BatteryManagerHelper.Changed -= batteryManager_Callback;

由于包装,事件处理程序不被视为相同的实例。

尽管这解释了您所看到的,但这不是正确的行为。它将在下一个版本中修复。

更新

您可以在此处关注此问题https://github.com/dot42/dot42/issues/13

于 2014-01-24T14:58:22.953 回答