2017-01-09 7 views
0

私はこの質問の変種を見たことがあると思いますが、「決定的な回答」はありません。以下のコードでは、SomeEventManagerがsomeImplClassTwo.myEventListenerAおよびsomeImplClassTwo.myEventListenerBへの参照を保持し、someImplClassTwoがガベージコレクションされないようにして、someEventManager.notifyListeners()が2回目に出力されることを理解しています。呼び出される。リスナーをJavaで自動的に登録解除する「クリーン」な方法はありますか?

しかし、SomeImplClassのユーザーは、実装に関与するリスナーがあることを知らなくてもいいし、これらのリスナーは手動で未登録(つまりSomeImplClass.releaseListeners())する必要があります。 SomeImplClassオブジェクトを解放します。

これを行うにはクリーンな/受け入れられた方法がありますか?

p.s.私は既にfun()のためにfinalize()を行っていますが、SomeImplClassのどちらのインスタンスに対してもGCがこの場合でも試行されていないことを確認しました。だから、それは潜在的な解決策としての非スターターではないようです。

テストドライバ

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

イベントインタフェース

public interface SomeEventListener { 
    public void handleSomeEvent(); 
} 

イベントマネージャ

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

イベントリスナーの実装

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

それがイベントを取得するまで、リスナーの周りスティックので、私はそれが完璧なソリューションだと言うではないでしょう、そしてそれはまだガベージコレクションに多少依存します。できるだけ明示的に削除するか、GUIコンポーネントのaddNotify/removeNotifyで置き換えようとしています。

+0

私は同様のルートを取っています.. 。今のところ。あなたのアプローチでは、ThingToUpdateのライフサイクルがリスナーのライフサイクルを超えないため、そのシナリオに固有のものです。 私のアプローチは、リスナー自体を弱い参照(具体的な実装ですか?)として保持していますが、私の必要性のために働きます。しかし、私はリスナーを内部クラスにすることはできません。外部クラスは強い参照を持ち、内部は暗黙の参照(強いと思われます)を外部に持ち込むことはできません。 Javaが内部クラスの外部参照のために弱い参照を使用していたとしたらどうでしょう? – SoCal

関連する問題