12

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

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

所以我已经实现WeakReference并保持活动实例使用WeakReference. Activity但是即使在活动被破坏后,我仍然看到实例还活着。

我创建了一个Handler内部活动并将活动实例作为对处理程序的弱引用传递。
到我Handler在 10 秒后回复发送给它的消息时,Activity已被销毁。但是弱引用仍然有Activity实例,我看到Toast, afterActivity被破坏了。

是不是有些地方我的理解有误?
有人可以解释如何处理传递给处理程序的消息,但 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();
}

在活动 onDestroy()

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

2 回答 2

5

你不需要WeakReference这里。可以只包含对的Handler引用Activity。在活动中onDestroy(),只需调用一个方法来设置对toMyHandler的引用。签入。ActivitynullnullhandleMessage()

另一种选择是:在活动的onDestroy()调用中,一个方法会中断睡眠线程,以便它在发送消息之前关闭。

于 2013-04-24T07:25:12.610 回答
3

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

我认为为了你的目的,有一条更好的路可以走。您可能想要做的是将活动附加分离并可能重新附加(例如,在配置更改时)您的服务。不要希望垃圾收集器为您完成工作。相反,明确地提出来。

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

更新

阅读您的评论后:您是对的,我没有具体解决这个问题。

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

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

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

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

于 2013-04-24T06:58:54.210 回答