2014-01-24 2 views
2

У меня есть статический класс под названием «BatteryManagerHelper», который контролирует состояние батареи с помощью намерения Intent.ACTION_BATTERY_CHANGED. Каждый раз, когда система уведомляет об изменении состояния батареи, мой класс улавливает изменения, сохраняет новые значения в общедоступных свойствах класса (заряд, статус, healt ..), затем поднимает событие «Изменено»Отказаться от подписки на мероприятие с dot42

общественное статическое событие Действие Изменено;

Каждая активность или класс в моем проекте могут подписаться на это событие с кодом:

BatteryManagerHelper.Changed += BatteryManagerHelper_Changed; 

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

он работает отлично.

Однако, когда действие происходит в OnPause() или OnDestory(), я хотел бы отказаться от подписки на событие Change, поэтому больше никаких уведомлений о моей деятельности.

Я попытался с

BatteryManagerHelper.Changed -= BatteryManagerHelper_Changed; 

и

BatteryManagerHelper.Changed -= null; 

и

BatteryManagerHelper.Changed += null; 

, но никто не похоже на работу. Я получаю событие также в Деятельности был разрушен из системы.

Итак, первый вопрос: как правильно отписаться от событий? Второй вопрос: почему разрушенное действие может продолжать получать события?

Примечание: код подписки и отписки находится на OnResume() и OnPause(). Когда действие приостановлено, возобновлено, я получаю два уведомления о событиях; на паузе паузы-возобновления я получаю три последовательных уведомления и так далее. Кажется, что + = добавляет новый прослушиватель событий, а - = не может удалить слушателя, сохраняя предыдущие ссылки слушателей в игре.

Это статический код класса. Я использовал статический класс, чтобы он мог быть доступен в любом месте кода. Статический класс имеет (obviuslly) частный экземпляр дочернего класса для обратных вызовов 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(); 
     } 
    } 
    } 

} 

Это код в моем фрагменте (этот фрагмент является заменой системы Toolbar, мое приложение является полноэкранным один), но вы также можете попробовать в классическом классе активность, о событиях 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 работает нормально.

Есть идеи по этому вопросу? Это правильно по дизайну?

благодаря

+0

Можете ли вы показать нам, где вы подписались и отписаться? Возможно, вы успешно отменили подписку, но новый экземпляр повторно подписался, например. в вашем OnCreate. –

+0

Привет, Франк, я улучшил свой пост с полным кодом. Вы можете попробовать самостоятельно мой статический класс с простой Activity и получить то же самое поведение: каждый новый + = добавляет новый listner правильно, но каждый - = не удаляет предыдущий справочник listner. – minimega

ответ

1

При компиляции этого кода:

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

BatteryManagerHelper.Changed += BatteryManagerHelper_Changed; 

Тогда ки не типа делегата, и он будет неявно обернут экземпляром действий. То же самое происходит, когда вы отказаться от подписки:

BatteryManagerHelper.Changed -= BatteryManagerHelper_Changed; 

Эта неявная упаковка не происходит, когда вы делаете это:

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

Из-за упаковки, обработчики событий не рассматриваются в качестве одних и тех же случаях.

Хотя это объясняет, что вы видите, это неправильное поведение. Это будет исправлено в следующем выпуске.

UPDATE

Вы можете следить за этим вопросом здесь https://github.com/dot42/dot42/issues/13.

+0

Спасибо за ваш ответ Frank, I ' используйте второй (рабочий) подход. Я жду следующего релиза, надеясь скоро! – minimega

+0

Эта проблема решается в версии 1.0.1.81, теперь доступной для загрузки. –

Смежные вопросы