0

因此,通过阅读/研究有关内存泄漏的信息,建议将所有内部类设为静态以避免内存泄漏。但是,通过查看 SDK 示例(特别是 TicTacToeLib),他们在不使用静态内部类的情况下实现了回调。这会导致内存泄漏吗?如果不是,为什么?

private Handler mHandler = new Handler(new MyHandlerCallback());


private class MyHandlerCallback implements Callback {
        public boolean handleMessage(Message msg) {
            if (msg.what == MSG_COMPUTER_TURN) {

                // Pick a non-used cell at random. That's about all the AI you need for this game.
                State[] data = mGameView.getData();
                int used = 0;
                while (used != 0x1F) {
                    int index = mRnd.nextInt(9);
                    if (((used >> index) & 1) == 0) {
                        used |= 1 << index;
                        if (data[index] == State.EMPTY) {
                            mGameView.setCell(index, mGameView.getCurrentPlayer());
                            break;
                        }
                    }
                }

                finishTurn();
                return true;
            }
            return false;
        }
    }
4

2 回答 2

1

是的,如果此示例将 a 保留Message在队列中,则会导致泄漏。但这不是一个非常严重的泄漏,因为它通常仅限于相当短的时间。

但是有一种相当简单的方法可以防止泄漏:

将以下两个类放入您的项目中

/** Callback that decouples the wrapped Callback via WeakReference */
public class SafeCallback implements Handler.Callback {
    private final WeakReference<Handler.Callback> mCallback;
    public SafeCallback(Handler.Callback callback) {
        mCallback = new WeakReference<Handler.Callback>(callback);
    }
    @Override
    public boolean handleMessage(Message msg) {
        Handler.Callback callback = mCallback.get();
        if (callback != null)
            return callback.handleMessage(msg);
        // else warn, return true, ..?
        return false;
    }
}

/** replacement for anonymous inner Handler implementations */
public abstract class SafeHandler implements Handler.Callback {
    @Override
    public abstract boolean handleMessage(Message msg);

    public final Handler get() {
        return new Handler(new SafeCallback(this));
    }
    public final Handler get(Looper looper) {
        return new Handler(looper, new SafeCallback(this));
    }
}

现在您几乎可以像以前一样使用Handler/Callback了,但它不再泄漏。

所以要么喜欢

public class TestActivity extends Activity {
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new SafeHandler() { // << instead of new Handler() {
            @Override
            public boolean handleMessage(Message msg) {
                // handle message
                return false;
            }
        }.get(); // << Notice this added .get()
    }
}

或喜欢

public class TestActivity2 extends Activity implements Handler.Callback {

    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(new SafeCallback(this)); // << wrapped in SafeCallback
    }

    @Override
    public boolean handleMessage(Message msg) {
        // handle message
        return false;
    }
}

泄漏问题Handler是每个Message/ Runnable(实际上被包裹在 a 中Message)都知道它的目标,即对Handleror有一个硬引用Callback。如果该目标是一个非静态内部类,它将隐含对外部类的硬引用,该外部类通常是一个Activity.

这意味着只要您的Messages排队Handler,您​​的整体Activity就不能被垃圾收集。

为了解决这个问题,必须打破从Messageto的硬引用链。Activity该类SafeCallback通过只WeakReference对您的Activity.

这意味着,Message现在有一个硬引用,SafeCallback但那里的部分绑定现在可以被垃圾收集。如果发生这种情况,结果会Handler.Callback callback = mCallback.get();被简单地丢弃。反正没有更有用的目标了。它仍在泄漏自身,但这是一个几乎是空的类,因此不会导致问题。nullMessageSafeCallback

于 2012-11-15T00:24:37.957 回答
-1

我会从你试图解决什么用例的角度来处理它,而不是语言本身在做什么。如果您“嵌套类”(不是内部类,因为内部类不能是静态的)需要能够在其父类上调用非静态方法,或者读取非静态成员,那么您别无选择,但使其成为非静态的。如果您可以避免不访问任何父类的非静态资源,那么请务必这样做(无论如何您都会节省一些内存)。但是,如果您担心内存泄漏并且要像示例中那样将嵌套类设为私有,那么您真的不应该 不必担心,因为该类的实例只能在父类本地创建(除非您创建父类的静态成员,该成员包含对嵌套类实例的引用,在这种情况下,该对象将是直到父类被VM卸载)。总之,我个人不会太担心您的嵌套类是声明为静态还是非静态,但如果您担心内存泄漏,请更关注该类实例的生命周期。

于 2012-11-15T00:20:13.880 回答