gpt4 book ai didi

android - 刷新消息队列以防止基于消息的泄漏。但是要刷新哪个 HandlerThread?

转载 作者:行者123 更新时间:2023-12-02 00:53:58 25 4
gpt4 key购买 nike

我基本上有描述的内存泄漏here由 LeakCanary 检测到。

LC stacktrace

我正在尝试通过使用上述帖子中的“管道工修复”来解决此问题,该帖子使用空消息刷新消息队列。每次空闲时提供的代码示例刷新消息队列。在我的对话框被解除后,我只需要刷新一次队列:

public class ExampleDialogFragment extends AppCompatDialogFragment {

public static ExampleDialogFragment newInstance() {
return new ExampleDialogFragment();
}

@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
return new AlertDialog.Builder(getContext())
.setPositiveButton(android.R.string.ok, (dialog, which) -> onClicked())
.create();
}

private void onClicked() {
if (getTargetFragment() instanceof Callbacks) {
((Callbacks) getTargetFragment()).onButtonClicked();
flushStackLocalLeaks();
}
}

private static void flushStackLocalLeaks() {
final Handler handler = new Handler(Looper.myLooper());
handler.post(() -> Looper.myQueue().addIdleHandler(() -> {
Timber.d("Flushing on thread %s", Thread.currentThread().getName());
handler.sendMessageDelayed(handler.obtainMessage(), 1000);
return false; // we only want to flush once, not *every* 1000 mSec
}));
}

public interface Callbacks {
void onButtonClicked();
}
}

我面临的问题是,在 LeakCanary 报告中,泄漏根源处的 HandlerThread 绝不是我期望的线程。有时是 ConnectivityThread , 有时是 HandlerThread , 其中 mName = "queued-work-looper" , 其他是 HandlerThread我的分析库使用。在这些情况下,它都不是主线程。我希望消息会从主线程泄漏。所以
  • 为什么这种泄漏发生在主线程以外的 HandlerThread/ConnectivityThread 上?
  • 我怎么知道哪个 HandlerThread 需要刷新?
  • 最佳答案

    我在 12 月在这里提交了问题:https://issuetracker.google.com/issues/146144484 .

    核心问题是每个空闲的 HandlerThread 都保留了他们最后一次回收的 Message。

    我们当前的解决方法是随着时间的推移找到新的处理程序线程并设置重复刷新:

    import android.os.Handler
    import android.os.HandlerThread
    import android.os.Looper
    import java.util.concurrent.Executors
    import java.util.concurrent.TimeUnit.SECONDS

    object AndroidLeaks {

    /**
    * HandlerThread instances keep local reference to their last handled message after recycling it.
    * That message is obtained by a dialog which sets on an OnClickListener on it and then never
    * recycles it, expecting it to be garbage collected but it ends up being held by the HandlerThread.
    */
    fun flushHandlerThreads() {
    val executor = Executors.newSingleThreadScheduledExecutor()

    val flushedThreadIds = mutableSetOf<Int>()
    // Wait 2 seconds then look for handler threads every 3 seconds.
    executor.scheduleWithFixedDelay({
    val newHandlerThreadsById = findAllHandlerThreads()
    .mapNotNull { thread ->
    val threadId = thread.threadId
    if (threadId == -1 || threadId in flushedThreadIds) {
    null
    } else {
    threadId to thread
    }
    }
    flushedThreadIds += newHandlerThreadsById.map { it.first }
    newHandlerThreadsById
    .map { it.second }
    .forEach { handlerThread ->
    var scheduleFlush = true
    val flushHandler = Handler(handlerThread.looper)
    flushHandler.onEachIdle {
    if (scheduleFlush) {
    scheduleFlush = false
    // When the Handler thread becomes idle, we post a message to force it to move.
    // Source: https://developer.squareup.com/blog/a-small-leak-will-sink-a-great-ship/
    try {
    flushHandler.postDelayed({
    // Right after this postDelayed executes, the idle handler will likely be called
    // again (if the queue is otherwise empty), so we'll need to schedule a flush
    // again.
    scheduleFlush = true
    }, 1000)
    } catch (ignored: RuntimeException) {
    // If the thread is quitting, posting to it will throw. There is no safe and atomic way
    // to check if a thread is quitting first then post it it.
    }
    }
    }
    }
    }, 2, 3, SECONDS)
    }

    private fun Handler.onEachIdle(onIdle: () -> Unit) {
    try {
    // Unfortunately Looper.getQueue() is API 23. Looper.myQueue() is API 1.
    // So we have to post to the handler thread to be able to obtain the queue for that
    // thread from within that thread.
    post {
    Looper
    .myQueue()
    .addIdleHandler {
    onIdle()
    true
    }
    }
    } catch (ignored: RuntimeException) {
    // If the thread is quitting, posting to it will throw. There is no safe and atomic way
    // to check if a thread is quitting first then post it it.
    }
    }

    private fun findAllHandlerThreads(): List<HandlerThread> {
    // Based on https://stackoverflow.com/a/1323480
    var rootGroup = Thread.currentThread().threadGroup!!
    while (rootGroup.parent != null) rootGroup = rootGroup.parent
    var threads = arrayOfNulls<Thread>(rootGroup.activeCount())
    while (rootGroup.enumerate(threads, true) == threads.size) {
    threads = arrayOfNulls(threads.size * 2)
    }
    return threads.mapNotNull { if (it is HandlerThread) it else null }
    }
    }

    关于android - 刷新消息队列以防止基于消息的泄漏。但是要刷新哪个 HandlerThread?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55659863/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com