2017-01-09 3 views
0

Я считаю, что я видел варианты этого вопроса, но не «окончательный ответ». В приведенном ниже коде я понимаю, что SomeEventManager содержит ссылку на someImplClassTwo.myEventListenerA и someImplClassTwo.myEventListenerB и что это не позволяет некоторому implClassTwo быть собранным мусором, и это приводит к выходу, сгенерированному во второй раз, когда someEventManager.notifyListeners() является вызывается.Есть ли «чистый» способ автоматической регистрации регистратора в Java?

Но я бы очень хотел, чтобы пользователи SomeImplClass не знали, что есть слушатели, участвующие в реализации, и что эти слушатели должны быть вручную незарегистрированы (например, SomeImplClass.releaseListeners()) до освобождая объект SomeImplClass.

Есть ли чистый/принятый способ сделать это?

p.s. Я уже играл с finalize(), просто для удовольствия, и подтвердил, что GC даже не пытался в этом случае, для любого экземпляра SomeImplClass. Таким образом, это, кажется, не стартер, как потенциальное решение.

Test Driver

public class TestDriver { 
    public static void main(String[] args) { 
     SomeEventManager someEventManager = SomeEventManager.getInstance(); 
     SomeImplClass someImplClassOne = new SomeImplClass("One"); 
     SomeImplClass someImplClassTwo = new SomeImplClass("Two"); 

     someEventManager.notifyListeners(); 

     someImplClassOne.releaseListeners(); 
     someImplClassOne = null; 
     someImplClassTwo = null; 
     try { 
      Thread.sleep(1000); 
     } catch(InterruptedException e) { 
     } 
     someEventManager.notifyListeners(); 
    } 
} 

Интерфейс Event

public interface SomeEventListener { 
    public void handleSomeEvent(); 
} 

Event-менеджер

import java.util.ArrayList; 
import java.util.List; 

public class SomeEventManager { 
    private static SomeEventManager  eventManager = null; 
    private  List<SomeEventListener> listeners = null; 

    private SomeEventManager() { 
     listeners = new ArrayList<SomeEventListener>(); 
    } 

    public static SomeEventManager getInstance() { 
     if (eventManager == null) { 
      eventManager = new SomeEventManager(); 
     } 
     return eventManager; 
    } 

    public void addListener(SomeEventListener listener) { 
     if (!listeners.contains(listener)) { 
      listeners.add(listener); 
     } 
    } 

    public void removeListener(SomeEventListener listener) { 
     listeners.remove(listener); 
    } 

    public void notifyListeners() { 
     for(SomeEventListener listener : listeners) { 
      listener.handleSomeEvent(); 
     } 
    } 
} 

Event Listener Реализация

public class SomeImplClass { 
    private InnerEventListener myEventListenerA = null; 
    private InnerEventListener myEventListenerB = null; 
    private String    id    = null; 

    public SomeImplClass(String id) { 
     this.id = id; 
     myEventListenerA = new InnerEventListener(id + "_A"); 
     myEventListenerB = new InnerEventListener(id + "_B"); 
    } 

    public void releaseListeners() { 
     myEventListenerA.unregisterListener(); 
     myEventListenerB.unregisterListener(); 
    } 

    private class InnerEventListener implements SomeEventListener { 
     private SomeEventManager someEventManager = null; 
     private String   id    = null; 

     public InnerEventListener(String id) { 
      someEventManager = SomeEventManager.getInstance(); 
      this.id   = id; 
      registerListener(); 
     } 

     public void registerListener() { 
      someEventManager.addListener(this); 
     } 

     public void unregisterListener() { 
      someEventManager.removeListener(this); 
     } 

     public void handleSomeEvent() { 
      System.out.println("InnerEventListener->" + id); 
     } 
    } 
} 
+0

См. ['WeakHashMap'] (http://stackoverflow.com/questions/5511279/what-is-a-weakhashmap-and-when-to-use-it) –

ответ

1

Решение, которое мы используем, состоит в том, чтобы слушатель автоматически отменил регистрацию, если он вызван, и предмет, который он обновил, был собран.

Это выглядит как это:

private static class InnerEventListener implements SomeEventListener { 
    private final WeakReference<ThingToUpdate> thingRef; 

    public InnerEventListener(ThingToUpdate thing) { 
     thingRef = new WeakReference<>(thing); 
    } 

    @Override 
    public void handleSomeEvent(SomeEvent event) { 
     ThingToUpdate thing = thingRef.get(); 
     if (thing != null) { 
      thing.updateSomehow(); 
     } else { 
      ((SomeEventedThing) event.getSource()) 
       .removeSomeEventListener(this); 
     } 
    } 
} 

//... 

SomeEventedThing eventedThing; 
ThingToUpdate thingToUpdate; 

//... 

eventedThing.addListener(new InnerEventListener(thingToUpdate)); 

Я бы не сказал, что это идеальное решение, потому что слушатель палочками вокруг до тех пор, пока он получает событие, и это все еще в некоторой степени зависит от сбора мусора. Мы пытались заменить его явным удалением, где это возможно, обычно на addNotify/removeNotify на компонентах графического интерфейса.

+0

Я беру аналогичный маршрут .. .на данный момент. Ваш подход предполагает, что жизненный цикл ThingToUpdate не распространяется за пределы слушателя, поэтому он специфичен для этого сценария. Мой подход удерживает слушателя как слабую ссылку (также конкретную реализацию?), Но работает для моей потребности. Но у меня не может быть слушателей внутренним классом, так как внешний класс содержит сильную ссылку, а внутреннюю неявную ссылку (предполагаемую сильную?) На внешнюю ... предотвращая сбор мусора. Может быть, было бы хорошо (проще?), Если бы Java использовала слабую ссылку для ссылки на внутренний класс? – SoCal