Java/Android : shortest way (lambda?) to pass a target handler to a custom button referencing this as a weak reference

huangapple 未分类评论57阅读模式
英文:

Java/Android : shortest way (lambda?) to pass a target handler to a custom button referencing this as a weak reference

问题

在Android中,我创建了自定义按钮。以前,我只是有一个非常简单的界面来传递目标方法作为按钮的回调:

public interface onTouchInterface {
    void onTouch(); 
}

当我想要将回调传递给按钮时,我只是使用了lambda表达式:

mybutton.addTarget(this::mycallback); 

这是一种快捷方式,等同于:

mybutton.addTarget(new onTouchInterface() { 
    @Override 
    void onTouch() { this.mycallback() }
});

问题是,我发现this(在我这种情况下通常是一个Fragment)在我从任何地方删除Fragment后仍然存在于内存中,尽管垃圾回收器应该已经将其删除。我发现它被我的lambda表达式“封装”起来。

我的问题是:如何在拥有弱引用的情况下编写最短的语法?目前,我创建了这个类来代替我以前的非常简单的接口:

public abstract static class Target<T>
{
    protected WeakReference<T> scope;

    static protected class ScopeNotPresentException extends Exception {}

    protected Target(T scope) { this.scope = new WeakReference<T>(scope); }

    // 这是按钮在用户点击时调用的原始回调
    // 如果作用域为null,则引发异常,否则执行onTouch(可重写)
    final public void onTouchRawCallback() throws ScopeNotPresentException {
        if(scope.get() == null)
            throw new ScopeNotPresentException();
        else
            onTouch(scope.get());
    }

    // 当执行这个子类化方法时,会验证作用域是否不为null
    protected void onTouch(T scope) { }
}

我的按钮类原始回调如下:

try {
    mTarget.onTouchRawCallback();
}
catch(Target.ScopeNotPresentException e){
    Log.w(LOG_TAG, "target button method was ignored because scope weak ref is null");
}

因此,如果scope.get()为null,则触发异常,不会调用onTouch()。最后,所有我的Fragment都像这样为它们的按钮添加处理程序:

mybutton.addTarget(new MyCustomButton.Target<MyFragment>(this) {
    @Override
    protected void onTouch(MyFragment scope) {
        scope.fragmentCustomClickHandler(); 
    }
});

但这当然比mybutton.addTarget(this::mycallback);要冗长得多。我发现对于带有传递给构造函数的参数的泛型类型的类,无法使用lambda。你有没有更好的实现方法,以便我可以继续使用lambda的简短语法,同时使用弱引用安全地检索this?(例如,在Swift中,可以使用addTarget({ [weak self] self?.myCallback(); }),如果self为null,就会停止执行)

英文:

I created my custom buttons in Android. Before, I just had a very simple interface to pass target method as a callback for my buttons :

public interface onTouchInterface {
    void onTouch(); 
}

When I wanted to pass a callback to a button, I just used lambda expression :

mybutton.addTarget(this::mycallback); 

which is a shortcut for

mybutton.addTarget(new onTouchInterface() { 
    @Override 
    void onTouch() { this.mycallback() }
});

The problem is that I realized that this (which is often a Fragment in my case) was still in the memory even after I removed my fragment from anywhere, while garbage collector should have removed it. I discovered it was "encapsuled" by my lambda expression.

My question is : how can I make the shortest syntax while having a weak reference for this ? For the moment, I created this class instead of my old very simple interface :

public abstract static class Target&lt;T&gt;
{
    protected WeakReference&lt;T&gt; scope ;

    static protected class ScopeNotPresentException extends Exception {}

    protected Target(T scope) { this.scope = new WeakReference&lt;T&gt;(scope); }

    // this is the raw callback called by the button when user clicks
    // it throws an exception if the scope is null, otherwise it executes onTouch (to be overrided)
    final public void onTouchRawCallback() throws ScopeNotPresentException {
        if(scope.get() == null)
            throw new ScopeNotPresentException();
        else
            onTouch(scope.get());
    }

    // when this subclassed method is executed, the scope is verified to be not null
    protected void onTouch(T scope) { }

} 

My button class raw callback does :

try {
    mTarget.onTouchRawCallback();
}
catch(Target.ScopeNotPresentException e){
    Log.w(LOG_TAG, &quot;target button method was ignored because scope weak ref is null&quot;);
} 

So, if scope.get() is null, the exception is triggered and onTouch() is not called. Finally, all my Fragment add a handler to their buttons like so :

mybutton.addTarget(new MyCustomButton.Target&lt;MyFragment&gt;(this) {
    @Override
    protected void onTouch(MyFragment scope) {
        scope.fragmentCustomClickHandler(); 
    }
});

But this is of course much more verbose than mybutton.addTarget(this::mycallback);. I saw that I couldn't use lambda for generic typed class with arguments passed to constructor.

Do you have any idea of a better implementation so I could keep using lambda short syntax while having a safe retrieval of this, using weak references ? (For exemple, in swift, it's
addTarget({ [weak self] self?.myCallback(); }) which stops if self is null)

huangapple
  • 本文由 发表于 2020年6月29日 09:19:11
  • 转载请务必保留本文链接:https://java.coder-hub.com/62629929.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定