即使在调用onDestroy()之后,活动实例仍然存在

时间:2013-04-24 06:41:40

标签: android android-activity android-service

我从Activity传递一个在mainUI线程上创建的处理程序并传递给执行某些网络操作的线程,当我获得结果时,我使用处理程序将结果发送回活动。

当我浏览这些链接时,这种方法在内存泄漏方面存在问题:
Inner ClassHandler Memory Leak
Android Developers

所以我实施了WeakReference,并使用WeakReference保留了活动实例。但即使活动被破坏,我仍然看到Activity实例仍然存在。

我创建了一个Handler内部活动,并将活动实例作为弱引用传递给处理程序。
当我的Handler响应10secs后传递给它的消息时,Activity被销毁。但弱引用仍然有Activity实例,在Toast被销毁后我看到了Activity

是否有一些我的理解错误?
有人可以解释如何处理传递给处理程序的消息,但UI不在吗?

import java.lang.ref.WeakReference;

import android.os.Handler;
import android.os.Message;

public abstract class SingleParamHandler <T> extends Handler
{
private WeakReference<T> mActivityReference;

public SingleParamHandler(T activity) {
    mActivityReference = new WeakReference<T>(activity);
}

@Override
public void handleMessage(Message msg) {
    if (mActivityReference.get() == null) {
        return;
    }
    handleMessage(mActivityReference.get(), msg);
}

protected abstract void handleMessage(T activity, Message msg);

}

import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.widget.Toast;

public class MainActivity extends Activity {

MyHandler<MainActivity> handler;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main1);
    handler = new MyHandler<MainActivity>(this);
    new Thread(new MyRunnable(handler)).start();
}

public void onDestroy() {
    super.onDestroy();
    System.out.println("######## Activity onDestroy() ###### ");
}

private class MyRunnable implements Runnable {
    private Handler mHandler;
    public MyRunnable(Handler handler) {
        mHandler = handler;
    }

    public void run() {
        try {
            Thread.sleep(10000);
            mHandler.sendMessage(Message.obtain(handler, 1));
        } catch ( Exception e) {
            e.printStackTrace();
        }
    }
}


private static class MyHandler<T> extends SingleParamHandler<T> {

    public MyHandler(T activity) {
        super(activity);
    }

    @Override
    public void handleMessage(T act, Message msg) {
        if(msg.what == 1) {
            Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();;
        }
    }
}

}

根据获得的回复,我在这里更新答案。你可以用你喜欢的方式做到这一点。但这是一种方式。

在SingleParamHandler

中添加了以下功能
public void clear() {
    mActivityReference.clear();
}

在Activity onDestroy()

public void onDestroy() {
    super.onDestroy();
    System.out.println("######## Activity onDestroy() ###### ");
    handler.clear();
}

2 个答案:

答案 0 :(得分:5)

此处您不需要WeakReferenceHandler只能包含对Activity的引用。在活动的onDestroy()中,只需在MyHandler上调用一个方法,即将Activity的引用设置为null。在null中查看handleMessage()

另一个选择是:在活动的onDestroy()中调用一个方法来中断休眠线程,使其在发送消息之前关闭。

答案 1 :(得分:3)

如果不需要,Android无法保证从内存中删除对象。换句话说,即使在调用onDestroy()之后,Activity对象也可以保留在内存中(如果有足够的可用内存)。另一方面,如果没有足够的内存,则无法保证将onDestroy() 调用;恰恰相反,Android可以在您当前的Activity上调用onPause()后取消整个过程(取决于Android版本)。

我认为为您的目的有更好的道路。您可能想要做的是附加分离并可能重新附加(例如,在配置更改中)活动到您的服务。不希望垃圾收集器为你做这项工作。相反,明确地说明。

子类Activity并覆盖生命周期方法以及startActivity()startActivityForResult(),让您的服务现在知道谁负责。当然,这只是一种尽力而为的方法,因为一些回调不能保证,但这只在某些不危险的情况下才有意义。例如,您的活动不会在onPause()中与您的服务分离,但它可能会在之后被杀死。但是你的服务在同一个进程中运行,所以它同时被杀死。或者它在不同的进程中运行,但Android会注意到连接断开,可能也可能不会终止服务;如果没有,那么你需要做的就是以强大的方式实现它,以便能够处理连接丢失。

<强>更新

阅读评论后:你是对的,我没有特别说明。

  

我正在弄清楚如何避免将消息发送到在被销毁的活动中创建的处理程序

鉴于上面的代码,并假设您真的只想显示Toast一个活动,只要它存在,以下方法应该有所帮助。

  • 如果您的Thread应该提供多个活动,请对其进行扩展,以便活动可以在创建后向线程注册。如果您的Thread只投放一个Activity,请将Activity引用与Handler引用一起传递到ThreadRunnable建设。
  • Thread通过Handler发送消息之前,请检查activity.isDestroyed()。如果未销毁活动,请发送消息。如果活动被销毁,请不要发送消息。
  • 根据您的线程是否应该服务多个活动,要么退出Runnable的{​​{1}}方法,要么将run() Activity引用设置为null Activity已被销毁。

这应该修复上面的代码。但是,如果您的方案增长,其他方法可能更合适。